
#include <iostream>
#include <map>
#include "ShaderProgram.h"
#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include "Texture2D.h"

#include "Camera.h"
#include "Render.h"

#include "Debuger.h"


constexpr float ScreenWidth = 1920.f;
constexpr float ScreenHeight = 1080.f;

static std::shared_ptr<FRender> GRender;

void FramebufferSizeCallback(GLFWwindow* window, int width, int height)
{
	GRender->OnFramebufferSizeChange(width, height);
}

void ProcessInput(GLFWwindow* window, float Dt)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
	{
		glfwSetWindowShouldClose(window, true);
	}
	static std::map<int32_t, float> FrontMoveScale{
		{GLFW_KEY_W , 1.f},
		{GLFW_KEY_S , -1.f}
	};

	static std::map<int32_t, float> RightMoveScale{
		{GLFW_KEY_A , -1.f},
		{GLFW_KEY_D , 1.f}
	};

	static constexpr float CameraSpeed = 10.f; // adjust accordingly

	const auto Camera = GRender->GetCamera();

	for (const auto& [Key, Scale] : FrontMoveScale)
	{
		if (glfwGetKey(window, Key) == GLFW_PRESS)
		{
			Camera->SetPosition(Camera->GetPosition() + Dt * Scale * CameraSpeed * Camera->GetFrontVector());
		}
	}

	for (const auto& [Key, Scale] : RightMoveScale)
	{
		if (glfwGetKey(window, Key) == GLFW_PRESS)
		{
			Camera->SetPosition(Camera->GetPosition() + Dt * Scale * CameraSpeed * Camera->GetRightVector());
		}
	}
}

void MouseCallback(GLFWwindow* Window, double Xpos, double Ypos)
{
	static double X = 0;
	static double Y = 0;

	if (static bool IsInit = false; !IsInit)
	{
		X = Xpos;
		Y = Ypos;

		IsInit = true;

		return;
	}

	constexpr float Sensitivity = 0.05f;

	const auto XOffset = static_cast<float>(Xpos - X) * Sensitivity;
	const auto YOffset = -static_cast<float>(Ypos - Y) * Sensitivity;

	const auto Camera = GRender->GetCamera();
	Camera->AddYawOffset(XOffset);
	Camera->AddPitchOffset(YOffset);

	X = Xpos;
	Y = Ypos;
}

void ScrollCallback(GLFWwindow* Window, double, double YOffset)
{
	const auto Camera = GRender->GetCamera();
	Camera->SetFOV(static_cast<float>(Camera->GetFOV() - YOffset));
}

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

int main(int Argc, char* Argv[])
{
	
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	const auto CurrentAppPath = std::filesystem::current_path();

	GRender = std::make_shared<FRender>();

	GLFWwindow* Window = glfwCreateWindow(static_cast<int>(ScreenWidth), static_cast<int>(ScreenHeight), "LearnOpenGL", nullptr, nullptr);
	if (!Window)
	{
		std::cout << "error create windows" << std::endl;
		glfwTerminate();
	}

	Debuger::getInstance()->init();

	DebugInfor("%hs", "Start Learn OpenGL ... ");

	glfwSetInputMode(Window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	glfwSetCursorPosCallback(Window, MouseCallback);

	glfwMakeContextCurrent(Window);

	if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	glfwSetFramebufferSizeCallback(Window, FramebufferSizeCallback);
	glfwSetScrollCallback(Window, ScrollCallback);

	GRender->Init(Window);

	GRender->OnFramebufferSizeChange(
		static_cast<int32_t>(ScreenWidth),
		static_cast<int32_t>(ScreenHeight));

	glEnable(GL_DEPTH_TEST);

	auto LastTime = glfwGetTime();

	while (!glfwWindowShouldClose(Window))
	{
		const auto CurrentTime = glfwGetTime();
		const auto DateTime = static_cast<float>(CurrentTime - LastTime);
		LastTime = CurrentTime;

		ProcessInput(Window, DateTime);

		GRender->BeginDraw();

		GRender->Draw(DateTime);

		GRender->EndDraw();
	}

	GRender->Destory();

	GRender = nullptr;

	Debuger::getInstance()->destory();

	glfwTerminate();

	return 0;
}
